// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package textproto

import (
	"bufio"
	"fmt"
	"io"
)

// 编写器实现了方便的方法来编写对文本协议网络连接的请求或响应。
type Writer struct {
	W   *bufio.Writer
	dot *dotWriter
}

// NewWriter返回一个新的Writer，该Writer向w写入。
func NewWriter(w *bufio.Writer) *Writer {
	return &Writer{W: w}
}

var crnl = []byte{'\r', '\n'}
var dotcrnl = []byte{'.', '\r', '\n'}

// PrintfLine写入格式化输出，后跟\r\n。
func (w *Writer) PrintfLine(format string, args ...interface{}) error {
	w.closeDot()
	fmt.Fprintf(w.W, format, args...)
	w.W.Write(crnl)
	return w.W.Flush()
}

// DotWriter返回一个可用于将点编码写入w的写入器。
// 它负责在必要时插入前导点，
// 将行结尾\r\n转换为\r\n，并在关闭DotWriter时添加最后一行。\r\n行
// 。调用方应该在下次调用w上的方法之前关闭
// DotWriter。
// 
// 有关点编码的详细信息，请参阅Reader的DotReader方法文档。
func (w *Writer) DotWriter() io.WriteCloser {
	w.closeDot()
	w.dot = &dotWriter{w: w}
	return w.dot
}

func (w *Writer) closeDot() {
	if w.dot != nil {
		w.dot.Close() // 设置w.dot=nil 
	}
}

type dotWriter struct {
	w     *Writer
	state int
}

const (
	wstateBegin     = iota // 初始状态；必须为零
	wstateBeginLine        // 行的开头
	wstateCR               // 写入\r（可能在行尾）
	wstateData             // 在行的中间写入数据
)

func (d *dotWriter) Write(b []byte) (n int, err error) {
	bw := d.w.W
	for n < len(b) {
		c := b[n]
		switch d.state {
		case wstateBegin, wstateBeginLine:
			d.state = wstateData
			if c == '.' {
				// 转义前导点
				bw.WriteByte('.')
			}
			fallthrough

		case wstateData:
			if c == '\r' {
				d.state = wstateCR
			}
			if c == '\n' {
				bw.WriteByte('\r')
				d.state = wstateBeginLine
			}

		case wstateCR:
			d.state = wstateData
			if c == '\n' {
				d.state = wstateBeginLine
			}
		}
		if err = bw.WriteByte(c); err != nil {
			break
		}
		n++
	}
	return
}

func (d *dotWriter) Close() error {
	if d.w.dot == d {
		d.w.dot = nil
	}
	bw := d.w.W
	switch d.state {
	default:
		bw.WriteByte('\r')
		fallthrough
	case wstateCR:
		bw.WriteByte('\n')
		fallthrough
	case wstateBeginLine:
		bw.Write(dotcrnl)
	}
	return bw.Flush()
}
